perf(desktop): stop re-rendering the app shell on every keystroke; fix two latent cache races it unmasked#1692
Merged
tlongwell-block merged 4 commits intoJul 10, 2026
Conversation
usePresenceSession stored raw Date.now() activity timestamps in React state and bumped them from capture-phase keydown/pointerdown listeners, so every keystroke anywhere in the app re-rendered AppShell (the app root) and swept context changes through the whole tree. Track activity in a ref and keep only the derived automatic status in state, updated solely when it actually flips (online <-> away, on activity, interval tick, or visibility change). Probe: AppShell renders during a 74-keystroke burst drop 70 -> 1. Benchmark keystroke input-to-paint medians halve across every scenario (32 -> 16ms channel, 48 -> 24ms in a 68-reply thread at 4x throttle); thread keystrokes over the 50ms frame budget drop 76 -> 1-3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lattened cache useEditMessageMutation's apply-on-success update patched only the flattened channelMessagesKey array. The channel window STORE is the source of truth: every live merge re-flattens it over that array, so the patched edit was silently reverted by the next live event. Masked until now because per-keystroke app-shell renders (fixed in the previous commit) kept re-deriving visible state; exposed as a deterministic human-edit-agent-content.spec.ts failure on Linux CI. Adds mapChannelWindowEvents (identity-preserving per-event transform) and routes the edit through it; unit-tested. Verified 24/24 in the Playwright Linux container where it previously failed 2/3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A background profile refetch started before an update (e.g. by a route mount, staleTime 30s) could resolve after the mutation's onSuccess wrote the fresh profile and clobber it with the pre-update snapshot — own avatar/name silently reverted until some later refetch. Masked historically by users-batch refetch churn from per-keystroke app renders; exposed as a messaging.spec.ts avatar flake once those renders were removed. Cancel profileQueryKey fetches in onMutate and again before the onSuccess write. Avatar spec: 10/10 locally (was ~60%), Linux container at main's pre-existing baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tlongwell-block
approved these changes
Jul 10, 2026
This was referenced Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every keystroke and click, anywhere in the app, re-rendered the entire app shell:
usePresenceSession(mounted in AppShell) stored rawDate.now()activity timestamps in React state, updated from capture-phasekeydown/pointerdownlisteners. This was the largest single contributor to typing feeling slow, on every surface. Removing it also unmasked two latent data bugs that had been hiding behind the constant repaints — both fixed here.What
mapChannelWindowEventsand routes the edit through the store. Deterministic CI failure before; 24/24 in the Playwright Linux container after.profileQueryKeyfetches around the write.Repro / Verify
Profiler → record → type a sentence in any composer → stop.
main: one AppShell-rooted commit per keystroke.Edit-revert repro (before the fix): edit any message while other events stream into the channel — the edit reverts. Profile repro: save an avatar, immediately navigate — it intermittently never appears.
Risk Assessment
Medium — three focused fixes on hot paths (presence hook, edit cache write, profile mutation), each behavior-identical by construction and A/B-validated. 2269 unit tests; 429/430 local smoke (the 1 failure reproduces identically on unmodified main); previously-failing specs soaked in the Playwright Linux container.
Related: #1652 (thread-panel render fixes), #1635 (markdown parse cache) — same investigation, independent changes.
🤖 Generated with Claude Code